package online.inote.mapper.utils;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Set;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;

/**
 * <p>
 * Desc:
 * </p>
 *
 * @author XQF SUI
 * @created 2018年1月31日 上午9:42:12
 * @version 1.0
 */
public class ClassUtils extends org.springframework.util.ClassUtils {

	private static final String RESOURCE_PATTERN = "/**/*.class";
	private static ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

	/**
	 * <p>
	 * Desc: 根据多过滤器扫描包下class
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月31日 上午9:50:07
	 * @param filter      过滤器
	 * @param packagePath 包路径
	 * @return
	 */
	public static Set<Class<?>> scanClassByMultiFilter(TypeFilter[] filters, String... packagePath) {

		if (packagePath == null || packagePath.length < 1) {
			return null;
		}

		Set<Class<?>> classSet = new HashSet<>();

		try {
			for (String path : packagePath) {
				String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
						+ convertClassNameToResourcePath(path.trim()) + RESOURCE_PATTERN;
				Resource[] resources = resourcePatternResolver.getResources(pattern);
				MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

				for (Resource resource : resources) {
					if (resource.isReadable()) {
						MetadataReader reader = readerFactory.getMetadataReader(resource);

						if (filters != null && filters.length > 0) {
							boolean b = true;

							for (TypeFilter filter : filters) {
								if (!filter.match(reader, readerFactory)) {
									b = false;
									break;
								}
							}

							if (b) {
								classSet.add(Class.forName(reader.getClassMetadata().getClassName()));
							}
						} else {
							classSet.add(Class.forName(reader.getClassMetadata().getClassName()));
						}
					}
				}
			}
		} catch (ClassNotFoundException | IOException e) {
			throw new RuntimeException(e);
		}

		return classSet;
	}

	/**
	 * <p>
	 * Desc: 根据单过滤器扫描包下class
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月31日 上午10:06:29
	 * @param filter      过滤器
	 * @param packagePath 包路径
	 * @return
	 */
	public static Set<Class<?>> scanClassByFilter(TypeFilter filter, String... packagePath) {

		if (filter == null) {
			return scanClassByMultiFilter(null, packagePath);
		}

		TypeFilter[] filters = { filter };

		return scanClassByMultiFilter(filters, packagePath);
	}

	/**
	 * <p>
	 * Desc: 扫描包下class
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月31日 上午9:52:47
	 * @param packagePath 包路径
	 * @return
	 */
	public static Set<Class<?>> scanClass(String... packagePath) {
		return scanClassByMultiFilter(null, packagePath);
	}

	/**
	 * <p>
	 * Desc: 根据多注解过滤器扫描包下class
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月31日 上午10:19:38
	 * @param annotations
	 * @param packagePath
	 * @return
	 */
	public static Set<Class<?>> scanClassByAnnotation(Class<? extends Annotation>[] annotations,
			String... packagePath) {

		if (annotations == null || annotations.length < 1) {
			scanClassByMultiFilter(null, packagePath);
		}

		int i = 0;
		TypeFilter[] filters = new TypeFilter[annotations.length];

		for (Class<? extends Annotation> annotation : annotations) {
			AnnotationTypeFilter filter = new AnnotationTypeFilter(annotation, false);
			filters[i++] = filter;
		}

		return scanClassByMultiFilter(filters, packagePath);
	}

	/**
	 * <p>
	 * Desc: 根据单注解过滤器扫描包下class
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月31日 上午10:21:00
	 * @param annotation
	 * @param packagePath
	 * @return
	 */
	public static Set<Class<?>> scanClassByAnnotation(Class<? extends Annotation> annotation, String... packagePath) {

		if (annotation == null) {
			scanClassByMultiFilter(null, packagePath);
		}

		TypeFilter[] filters = { new AnnotationTypeFilter(annotation, false) };

		return scanClassByMultiFilter(filters, packagePath);
	}

	/**
	 * <p>
	 * Desc: 根据父类扫描class
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月31日 上午10:22:51
	 * @param clazz
	 * @param packagePath
	 * @return
	 */
	public static Set<Class<?>> scanClassBySuperClass(Class<?> clazz, String... packagePath) {
		TypeFilter[] filters = { new AssignableTypeFilter(clazz) };
		return scanClassByMultiFilter(filters, packagePath);
	}

	/**
	 * <p>
	 * Desc: 获取接口或类的泛型
	 * </p>
	 *
	 * @author Sui
	 * @created 2017年12月10日 下午11:00:23
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> Class<T> getGeneric(Class<?> clazz) {
		Type genType;

		if (clazz.isInterface()) {
			genType = clazz.getGenericInterfaces()[0];
		} else {
			genType = clazz.getGenericSuperclass();
		}

		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

		return (Class<T>) params[0];
	}

	/**
	 * <p>
	 * Desc: 获取类路径
	 * </p>
	 *
	 * @author Sui
	 * @created 2019年3月10日 下午1:49:28
	 * @param className
	 * @return
	 */
	public static String getClassPath(Class<?> clazz) {
		return clazz.getName().replace(".", "/");
	}
}